package com.cgs.query.handler.statement

import com.cgs.query.configuration.QueryConfiguration
import com.cgs.query.domain.BoundSql
import com.cgs.query.domain.ExecutionObj
import com.cgs.query.exception.QueryException
import com.cgs.query.executor.ExecutionContext
import com.cgs.query.handler.StatementHandler
import com.cgs.query.util.CastUtils
import java.sql.Connection
import java.sql.PreparedStatement
import java.sql.ResultSet
import java.sql.Statement

class PaginationStatementHandler(
        val config: QueryConfiguration,
        executionContext: ExecutionContext
) : StatementHandler {
    override val boundSql: BoundSql
    override val parameterHandler = config.parameterHandler
    private val mappedStatement = executionContext.mappedStatement
    private val dialect: Dialect = Dialect.getDialectByDbType(executionContext.dbType
            ?: throw QueryException("该数据库驱动不支持databaseProductName"))
    private val originSql: String
    private val countSql: String
    private val paginateSql: String
    val param = executionContext.param
    private val filterParam = param.filterKeys { it == "pn" || it == "ps" }
    private lateinit var countStatement: PreparedStatement
    private val pn: Int
    private val ps: Int
    private val startAndEnd: Pair<Int, Int>

    init {
        pn = CastUtils.any2Int(param["pn"])
        ps = CastUtils.any2Int(param["ps"])
        this.boundSql = parameterHandler.resolve(mappedStatement.executionObj, filterParam, executionContext)
        this.originSql = this.boundSql.sql
        this.paginateSql = dialect.toPaginateSelect(this.originSql)
        this.countSql = dialect.toCountSelect(this.originSql)
        this.startAndEnd = dialect.getStartAndEnd(pn, ps)
    }

    override fun prepare(connection: Connection): Statement {
        this.countStatement = connection.prepareStatement(countSql)
        return connection.prepareStatement(paginateSql)
    }

    override fun parameterize(statement: Statement) {
        //分页插入
        val size = this.boundSql.parameterMappings.size
        val ps = statement as PreparedStatement
        this.boundSql.parameterMappings.forEachIndexed { i, v ->
            ps.setObject(i + 1, v.value)
        }.apply {
            ps.setObject(size + 1, startAndEnd.first)
            ps.setObject(size + 2, startAndEnd.second)
        }
        //count插入
        this.boundSql.parameterMappings.forEachIndexed { i, v ->
            countStatement.setObject(i + 1, v.value)
        }
    }

    override fun update(statement: Statement): Int {
        TODO()
    }

    override fun query(statement: Statement): List<Any> {
        TODO()
    }

    override fun execute(statement: Statement): List<Any> {
        val ps = statement as PreparedStatement
        val paginateResult = this.config.resultSetHandler.handleResultSet(ps.execute(), ps, mappedStatement.limit)
        countStatement.execute()
        val count = getCount(countStatement.resultSet)
        return if (mappedStatement.limit == null) {
            listOf(mapOf("count" to count, "content" to paginateResult))
        } else {
            listOf(listOf(mapOf("count" to count)), paginateResult)
        }
    }

    override fun close(statement: Statement?) {
        statement?.close()
        countStatement.close()
    }

    private fun getCount(countResult: ResultSet): Long {
        return if (countResult.next())
            countResult.getLong(1)
        else
            -1L
    }
}